/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ext_dev.h"
#include "gpio_if.h"

#include <sys/time.h>

#define TRIG_PULSE_DURATION 1
#define SONIC_SPEED 340 // unit m/s
#define TRIG_GPIO 4
#define ECHO_GPIO 2
#define MICROSECONDS 1000000
#define METER_2_CENTIMETER 100

static ExtDevHcSr04State g_extDevHcSr04State = ULT_RANG_ST_CLOSE;
static uint64_t g_echoDuration = 0;

static unsigned long GetMicrosecond(void)
{
    struct timeval tv = {0};
    gettimeofday(&tv, NULL);

    return tv.tv_sec * MICROSECONDS + tv.tv_usec;
}

static int32_t ExtDevInterHcSr04FallingIrqHandler(uint16_t gpio, void *data)
{
    if (ECHO_GPIO != gpio) {
        LOG_E("unexpected irq from gpio %d.", gpio);
        return HDF_FAILURE;
    }
    g_echoDuration = GetMicrosecond() - g_echoDuration;
    g_extDevHcSr04State = ULT_RANG_ST_MEASURED;
    if (HDF_SUCCESS != GpioUnsetIrq(ECHO_GPIO, NULL)) {
        LOG_E("Unset HcSr04 echo pin irq failed!");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static int32_t ExtDevInterHcSr04RisingIrqHandler(uint16_t gpio, void *data)
{
    if (ECHO_GPIO != gpio) {
        LOG_E("unexpected irq from gpio %d.", gpio);
        return HDF_FAILURE;
    }
    g_echoDuration = GetMicrosecond();
    if (HDF_SUCCESS != GpioUnsetIrq(ECHO_GPIO, NULL)) {
        LOG_E("Unset HcSr04 echo pin irq failed!");
        goto HC_SR04_RASING_IRQ_ERR;
    }
    if (HDF_SUCCESS != GpioSetIrq(ECHO_GPIO, OSAL_IRQF_TRIGGER_FALLING, ExtDevInterHcSr04FallingIrqHandler, NULL)) {
        LOG_E("Set HcSr04 echo pin irq failed!");
        goto HC_SR04_RASING_IRQ_ERR;
    }
    if (HDF_SUCCESS != GpioEnableIrq(ECHO_GPIO)) {
        LOG_E("enable HcSr04 echo pin irq failed!");
        GpioUnsetIrq(ECHO_GPIO, NULL);
        goto HC_SR04_RASING_IRQ_ERR;
    }
    return HDF_SUCCESS;
HC_SR04_RASING_IRQ_ERR:
    g_extDevHcSr04State = ULT_RANG_ST_ERROR;
    return HDF_FAILURE;
}

ExtDevHcSr04State ExtDevGetHcSr04State(void)
{
    return g_extDevHcSr04State;
}

///超声波测距模块初始化
ExtDevCtlRst ExtDevHcSr04Init(void)
{
    if (HDF_SUCCESS != GpioSetDir(TRIG_GPIO, GPIO_DIR_OUT)) {
        LOG_E("Set HcSr04 trig pin output failed!");
        goto HC_SR04_INIT_ERR;
    }
    if (HDF_SUCCESS != GpioWrite(TRIG_GPIO, GPIO_VAL_LOW)) {
        LOG_E("Set HcSr04 trig pin%d low failed!", TRIG_GPIO);
        goto HC_SR04_INIT_ERR;
    }
    if (HDF_SUCCESS != GpioSetDir(ECHO_GPIO, GPIO_DIR_IN)) {
        LOG_E("Set HcSr04 echo pin input failed!");
        goto HC_SR04_INIT_ERR;
    }
    g_extDevHcSr04State = ULT_RANG_ST_STANDBY;
    return EXT_DEV_SUCCESS;
HC_SR04_INIT_ERR:
    g_extDevHcSr04State = ULT_RANG_ST_ERROR;
    return EXT_DEV_ERROR;
}
/// 超声波测距开始
ExtDevCtlRst ExtDevHcSr04Start(void)
{
    // 设置echo pin接收中断
    if (HDF_SUCCESS != GpioSetIrq(ECHO_GPIO, OSAL_IRQF_TRIGGER_RISING, ExtDevInterHcSr04RisingIrqHandler, NULL)) {
        LOG_E("Set HcSr04 echo pin irq failed!");
        goto HC_SR04_START_ERR;
    }
    if (HDF_SUCCESS != GpioEnableIrq(ECHO_GPIO)) {
        LOG_E("enable HcSr04 echo pin irq failed!");
        GpioUnsetIrq(ECHO_GPIO, NULL);
        goto HC_SR04_START_ERR;
    }
    // 设置高电平脉冲通知测距开始
    if (HDF_SUCCESS != GpioWrite(TRIG_GPIO, GPIO_VAL_HIGH)) {
        LOG_E("Set HcSr04 trig pin high failed!");
        goto HC_SR04_START_ERR;
    }
    osDelay(TRIG_PULSE_DURATION);
    if (HDF_SUCCESS != GpioWrite(TRIG_GPIO, GPIO_VAL_LOW)) {
        LOG_E("Set HcSr04 trig pin%d low failed!", TRIG_GPIO);
        goto HC_SR04_START_ERR;
    }
    g_extDevHcSr04State = ULT_RANG_ST_MEASURING;
    return EXT_DEV_SUCCESS;
HC_SR04_START_ERR:
    g_extDevHcSr04State = ULT_RANG_ST_ERROR;
    return EXT_DEV_ERROR;
}
/// 获取超声波测距结果
int32_t ExtDevGetHcSr04MsrRst(void)
{
    if (ULT_RANG_ST_MEASURED != g_extDevHcSr04State) {
        LOG_E("HcSr04 is not in measured state!");
        return EXT_DEV_ERROR;
    }
    g_extDevHcSr04State = ULT_RANG_ST_STANDBY;
    return (g_echoDuration * METER_2_CENTIMETER * SONIC_SPEED / MICROSECONDS / 2);
}
